package tech.dongling.boot.plugins;

import tech.dongling.boot.anno.Order;
import tech.dongling.boot.common.base.ApiResponse;
import tech.dongling.boot.common.base.HttpBizRequest;
import tech.dongling.boot.plugins.impl.GuavaLimiter;

import java.util.*;

public class PluginManager {
    private final List<RequestPlugin> requestPlugins = new ArrayList<>();
    private final List<ResponsePlugin> responsePlugins = new ArrayList<>();
    private final Limiter limiter;

    public PluginManager(Properties config) {
        loadRequestPlugins(config);
        loadResponsePlugins(config);
        this.limiter = loadLimiter(config);
    }

    private void loadRequestPlugins(Properties config) {
        ServiceLoader<RequestPlugin> loader = ServiceLoader.load(RequestPlugin.class);
        loader.forEach(plugin -> {
            plugin.init(config);
            requestPlugins.add(plugin);
        });
        sortPlugins(requestPlugins);
    }

    private void loadResponsePlugins(Properties config) {
        ServiceLoader<ResponsePlugin> loader = ServiceLoader.load(ResponsePlugin.class);
        loader.forEach(plugin -> {
            plugin.init(config);
            responsePlugins.add(plugin);
        });
        sortPlugins(responsePlugins);
    }

    private Limiter loadLimiter(Properties config) {
        GuavaLimiter limiter = new GuavaLimiter();
        limiter.init(config);
        return limiter;
    }

    private void sortPlugins(List<?> plugins) {
        plugins.sort((p1, p2) -> {
            int o1 = getOrder(p1);
            int o2 = getOrder(p2);
            return Integer.compare(o1, o2);
        });
    }

    private int getOrder(Object plugin) {
        return plugin.getClass().isAnnotationPresent(Order.class) ?
               plugin.getClass().getAnnotation(Order.class).value() : Integer.MAX_VALUE;
    }

    public void executeRequestPlugins(HttpBizRequest request) throws Exception {
        for (RequestPlugin plugin : requestPlugins) {
            plugin.process(request);
        }
    }

    public void executeResponsePlugins(ApiResponse response) throws Exception {
        for (ResponsePlugin plugin : responsePlugins) {
            plugin.process(response);
        }
    }

    public Limiter getLimiter() {
        return limiter;
    }
}